package butines.kraz;

import java.awt.Color;
import java.awt.Graphics2D;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import butines.core.GameSystem;
import butines.core.Input;
import butines.core.game.MessageDispatcher;
import butines.core.game.Neighborhood;
import butines.core.game.Vehicle;
import butines.core.game.graph.Graph;
import butines.core.game.pathfinder.NavNode;
import butines.core.math.Vector2f;
import butines.kraz.entity.Corpse;
import butines.kraz.entity.MessageType;
import butines.kraz.entity.Projectile;
import butines.kraz.entity.Unit;

public class Kraz implements Neighborhood {

	private final static int TILE_SIZE = 32;
	
	private MessageDispatcher messageDispatcher = new MessageDispatcher();

	private List<Unit> unitList = new LinkedList<Unit>();
	
	private List<Projectile> projectileList = new LinkedList<Projectile>();
	
	private List<Corpse> corpseList = new LinkedList<Corpse>();
	
	private UnitDecorator unitDecorator = new UnitDecorator();

	private Graph<NavNode> mapGraph;
	private NavNode[][] cells;
	
	private Unit selected;
	
	private Unit over;
	
	private Selector selector = new Selector();
	private View view = new View();
	
	public Kraz(int width, int height) {
		final int screenWidth = GameSystem.getContext().getScreenWidth();
		final int screenHeight = GameSystem.getContext().getScreenHeight();
		
		view.setView(0, 0, screenWidth, screenHeight);
		view.setScreen(screenWidth, screenHeight);
		view.setInner(width, height);
		
		mapGraph = GraphBuilder.createGraph(width, height, TILE_SIZE);
	
		cells = new NavNode[height/TILE_SIZE][width/TILE_SIZE];
		
		for (NavNode node: mapGraph.getNodes()) {
			if (node != null) {
				int x = (int)node.getX() / TILE_SIZE;
				int y = (int)node.getY() / TILE_SIZE;
				cells[y][x] = node;
			}
		}
		
	}
	
	private NavNode getCell(float x, float y) {
		int ix = (int)x / TILE_SIZE;
		int iy = (int)y / TILE_SIZE;
		return cells[iy][ix];
	}
	
	public Graph<NavNode> getMapGraph() {
		return mapGraph;
	}
	
	public void addUnit(Unit unit) {
		unit.setKraz(this);
		unitList.add(unit);
	}
	
	public void addProjectile(Projectile projectile) {
		projectileList.add(projectile);
	}

	private void updateCorpses(float tpf) {
		Iterator<Corpse> i = corpseList.iterator();
		
		while (i.hasNext()) {
			Corpse corpse = i.next();

			if (corpse.isAlive()) {
				corpse.update(tpf);
			} else {
				i.remove();
			}
			
		}
		
	}
	
	public void enforceNonPenetrationContraint(Unit unit) {
		
		for (Unit other: unitList) {
			if (unit.collideWith(other)) {
				unit.enforceNonPenetrationContraint(other);
				messageDispatcher.dispatch(MessageType.COLLIDED_WITH, other, unit, 0);
			}
		}
		
	}
	
	private void updateUnits(float tpf) {
		Iterator<Unit> i = unitList.iterator();
		while (i.hasNext()) {
			Unit unit = i.next();

			if (unit.isAlive()) {
				unit.update(tpf);
				enforceNonPenetrationContraint(unit);
			} else {
				i.remove();
				Corpse corpse = new Corpse(unit);
				corpseList.add(corpse);
			}

		}
	}
	
	private void updateProjectiles(float tpf) {
		Iterator<Projectile> i = projectileList.iterator();
		while (i.hasNext()) {
			Projectile projectile = i.next();
			if (projectile.isAlive()) {
				projectile.update(tpf);
			} else {
				i.remove();
			}
		}
	}
	
	
	public void update(float tpf) {
		view.scroll(GameSystem.getContext().getInput(), tpf);
		
		messageDispatcher.dispatchDelayedMessages();

		updateCorpses(tpf);
		
		updateUnits(tpf);
		
		updateProjectiles(tpf);

	}
	
	private void drawCorpses(Graphics2D g, float offsetX, float offsetY) {
		for(Corpse corpse: corpseList) {
			corpse.draw(g, offsetX, offsetY);
		}
	}
	
	private void drawUnits(Graphics2D g, float offsetX, float offsetY) {
		Iterator<Unit> i;
		
		Collections.sort(unitList, new Comparator<Unit>() {

			public int compare(Unit a, Unit b) {
				if (a.getPosition().y < b.getPosition().y) {
					return -1;
				}
				if (a.getPosition().y > b.getPosition().y) {
					return 1;
				}
				return 0;
			}

		});
		i = unitList.iterator();
		while (i.hasNext()) {
			Unit unit = i.next();
			unit.draw(g, offsetX, offsetY);
		}

		i = unitList.iterator();
		while (i.hasNext()) {
			Unit unit = i.next();
//			unitDecorator.drawArea(g, offsetX, offsetY, unit);
			unitDecorator.drawHealthBar(g, offsetX, offsetY, unit);
			unitDecorator.drawCooldownBar(g, offsetX, offsetY, unit);
			unitDecorator.drawState(g, offsetX, offsetY, unit);
//			unitDecorator.drawArriveSteeringForce(g, offsetX, offsetY, unit);
//			unitDecorator.drawSteerAvoidance(g, offsetX, offsetY, unit);
		}
	}

	public void drawProjectiles(Graphics2D g, float offsetX, float offsetY) {
		for(Projectile projectile: projectileList) {
			projectile.draw(g, offsetX, offsetY);
//			unitDecorator.drawAreaProjectile(g, offsetX, offsetY, projectile);
		}
	}
		
	
	private void drawCircle(Graphics2D g, float x, float y, float r) {
		int drawX = Math.round(x);
		int drawY = Math.round(y);
		int drawR = Math.round(r);
		g.drawOval(drawX - drawR, drawY - drawR, drawR * 2, drawR * 2);
	}
	
	public void draw(Graphics2D g) {
				
		final Graphics2D viewGraphics = view.createGraphics(g);
		final float offsetX = view.getOffsetX(); 
		final float offsetY = view.getOffsetY();
		
//		int xstart = (int)offsetX / TILE_SIZE;
//		int xend   =  xstart + view.getViewWidth() / TILE_SIZE;
//		
//		int ystart = (int)offsetY / TILE_SIZE;
//		int yend   = ystart + view.getViewHeight() / TILE_SIZE;
//		
//		viewGraphics.setColor(Color.yellow);
//		for (int j = ystart; j <= yend; j++) {
//			for (int i = xstart; i <= xend; i++) {
//				if (cells[j][i] != null) {
//					viewGraphics.drawOval(Math.round(cells[j][i].getX()-3), Math.round(cells[j][i].getY()-3), 5, 5);
//				}
//			}
//		}
		
		drawCorpses(viewGraphics, offsetX, offsetY);
		
		viewGraphics.setColor(Color.GREEN);
		if (over != null) {
			float x =  over.getPosition().x - offsetX;
			float y =  over.getPosition().y - offsetY;
			drawCircle(viewGraphics, x,  y, over.getRadius());
		}
		
		if (selected != null) {
			float x =  selected.getPosition().x - offsetX;
			float y =  selected.getPosition().y - offsetY;
			drawCircle(viewGraphics, x, y, selected.getRadius());
		}
		
		drawUnits(viewGraphics, offsetX, offsetY);

		drawProjectiles(viewGraphics, offsetX, offsetY);

		selector.draw(viewGraphics);
		
		viewGraphics.dispose();
		
	}

	public MessageDispatcher getMessageDispatcher() {
		return messageDispatcher;
	}

	public List<Unit> getUnitList() {
		return unitList;
	}

	private ArrayList<Vehicle> foundUnits = new ArrayList<Vehicle>();
	
	public Collection<Vehicle> findNearest(float ox, float oy, float radius) {
		foundUnits.clear();
		for (Unit unit: unitList) {
			float dx = ox - unit.getPosition().x;
			float dy = oy - unit.getPosition().y;
			float radiusExp = radius + unit.getRadius();
			if (dx * dx + dy * dy <= radiusExp * radiusExp) {
				foundUnits.add(unit);
			}
		}
		return foundUnits;
	}

	public void onMove(int x, int y) {
		selector.onMove(x, y);
		
		for (Unit unit: unitList) {
			if (unit.isDied() || !unit.isAlive()) {
				continue;
			}
			float posx = unit.getPosition().x;
			float posy = unit.getPosition().y;
			float r = unit.getRadius();
			if (selector.isMouseOver(posx, posy, r)) {
				over = unit;
				return;
			}
		}
		
		over = null;
		
		
	}
	
	public Unit getUnitByMouseOver() {
		for (Unit unit: unitList) {
			if (unit.isDied() || !unit.isAlive()) {
				continue;
			}
			float posx = unit.getPosition().x;
			float posy = unit.getPosition().y;
			float r = unit.getRadius();
			if (selector.isMouseOver(posx, posy, r)) {
				return unit;
			}
		}
		return null;
	}
	
	public void onButton(int x, int y, int button, boolean pressed) {
		if (button == Input.BUTTON_1) {
			if (pressed) {
				view.setScrollEnabled(false);
				selector.onPressed(x, y);
				selected = null;
			} else {
				view.setScrollEnabled(true);
				selector.onReleased();
				selected = getUnitByMouseOver();
			}
		}
		if (button == Input.BUTTON_3){
			if (selected == null) {
				return;
			}
			if (pressed) {
				Unit enemy = getUnitByMouseOver();
				if (enemy == null) {
					NavNode n = getCell(x, y);
					if (n != null) {
						selected.move(new Vector2f(n.getX(), n.getY()));
					}
				} else {
					selected.attack(enemy);
				}			
			}
		}
	}
	
	
}
